package com.glp.work.flow.contract.builder;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
import org.apache.poi.xwpf.usermodel.XWPFTableRow;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTBookmark;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.NestedIOException;
import org.springframework.core.io.Resource;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class ContractFactoryBean implements InitializingBean {
  
  private Map<String, ContractSource> sources = new HashMap<String, ContractSource>();
  private ContractBuilder builder = new ContractBuilder();

  private Resource[] configLocations;

  @Override
  public void afterPropertiesSet() throws Exception {
    init();
    log.info("Contract factory is inited.");
  }

  public ContractSource getSource(String id) {
    ContractSource source = sources.get(id);
    if (source == null) {
      throw new RuntimeException("Contract source is null. Template id is " + id);
    }
    return source.clone();
  }
  
  public void init() throws Exception {
    for (Resource configLocation : configLocations) {
      try {
    	  log.info("Contract parse resource file. File name is "+configLocation.getFilename());
        
        InputStream is = configLocation.getInputStream();
        List<ContractSource> contractSources = builder.build(is);
  
        for (ContractSource source : contractSources) {
          XWPFDocument document = source.buildDocument();
  
          validate(source, document);
  
          if (sources.get(source.getId()) != null) {
            throw new NestedIOException("Failed to init contract template '" + source.getId() + "'. Contract id is exits.");
          }
          sources.put(source.getId(), source);
        }
      } catch (Exception ex) {
        log.error("Contract parse resource file exception. Cause:", ex);
        throw new RuntimeException(ex);
      }
    }
  }

  private void validate(ContractSource source, XWPFDocument document) throws NestedIOException {
    List<Mark> marks = source.getMarks();

    Set<String> exits = new HashSet<String>();
    List<XWPFParagraph> paragraphs = document.getParagraphs();
    for (XWPFParagraph parag : paragraphs) {
      List<CTBookmark> bookmarks = parag.getCTP().getBookmarkStartList();
      for (CTBookmark mark : bookmarks) {
        for (Mark mk : marks) {
          if (mark.getName().equals(mk.getName())) {
            exits.add(mk.getName());
          }
        }
      }
    }

    List<XWPFTable> tables = document.getTables();
    for (XWPFTable table : tables) {
      List<XWPFTableRow> rows = table.getRows();
      for (XWPFTableRow row : rows) {
        List<XWPFTableCell> cells = row.getTableCells();
        for (XWPFTableCell cell : cells) {
          List<XWPFParagraph> cellparagraphs = cell.getParagraphs();
          for (XWPFParagraph parag : cellparagraphs) {
            List<CTBookmark> bookmarks = parag.getCTP().getBookmarkStartList();
            for (CTBookmark mark : bookmarks) {
              for (Mark mk : marks) {
                if (mark.getName().equals(mk.getName())) {
                  exits.add(mk.getName());
                }
              }
            }
          }
        }
      }
    }

    if (marks.size() != exits.size()) {
      List<String> miss = new ArrayList<String>();
      for (Mark mark : marks) {
        if (!exits.contains(mark.getName())) {
          miss.add(mark.getName());
        }
      }
      throw new NestedIOException(
          "Failed to validate contract template " + source.getId() + ". Params not exits " + Arrays.toString(miss.toArray()));
    }
  }

  public void setConfigLocations(Resource[] configLocations) {
    this.configLocations = configLocations;
  }

}
